import type { PrismaConfigInternal } from '@prisma/config'
import {
  arg,
  canPrompt,
  checkUnsupportedDataProxy,
  Command,
  createSchemaPathInput,
  format,
  getSchemaDatasourceProvider,
  HelpError,
  inferDirectoryConfig,
  isError,
  loadSchemaContext,
  MigrateTypes,
  validatePrismaConfigWithDatasource,
} from '@prisma/internals'
import { bold, dim, green, italic, red } from 'kleur/colors'
import prompt from 'prompts'

import { Migrate } from '../Migrate'
import { aiAgentConfirmationCheckpoint } from '../utils/ai-safety'
import { ensureDatabaseExists, parseDatasourceInfo } from '../utils/ensureDatabaseExists'
import { MigrateResetEnvNonInteractiveError } from '../utils/errors'
import { printDatasource } from '../utils/printDatasource'
import { printFilesFromMigrationIds } from '../utils/printFiles'

export class MigrateReset implements Command {
  public static new(): MigrateReset {
    return new MigrateReset()
  }

  private static help = format(`
Reset your database and apply all migrations, all data will be lost

${bold('Usage')}

  ${dim('$')} prisma migrate reset [options]

  The datasource URL configuration is read from the Prisma config file (e.g., ${italic('prisma.config.ts')}).

${bold('Options')}

       -h, --help   Display this help message
         --config   Custom path to your Prisma config file
         --schema   Custom path to your Prisma schema
      -f, --force   Skip the confirmation prompt

${bold('Examples')}

  Reset your database and apply all migrations, all data will be lost
  ${dim('$')} prisma migrate reset

  Specify a schema
  ${dim('$')} prisma migrate reset --schema=./schema.prisma

  Use --force to skip the confirmation prompt
  ${dim('$')} prisma migrate reset --force
  `)

  public async parse(argv: string[], config: PrismaConfigInternal, baseDir: string): Promise<string | Error> {
    const args = arg(argv, {
      '--help': Boolean,
      '-h': '--help',
      '--force': Boolean,
      '-f': '--force',
      '--schema': String,
      '--config': String,
      '--telemetry-information': String,
    })

    if (isError(args)) {
      return this.help(args.message)
    }

    if (args['--help']) {
      return this.help()
    }

    const schemaContext = await loadSchemaContext({
      schemaPath: createSchemaPathInput({
        schemaPathFromArgs: args['--schema'],
        schemaPathFromConfig: config.schema,
        baseDir,
      }),
    })

    const cmd = 'migrate reset'
    const validatedConfig = validatePrismaConfigWithDatasource({ config, cmd })

    const { migrationsDirPath } = inferDirectoryConfig(schemaContext, config)
    const datasourceInfo = parseDatasourceInfo(schemaContext.primaryDatasource, validatedConfig)

    printDatasource({ datasourceInfo })
    checkUnsupportedDataProxy({ cmd, validatedConfig })

    // TODO: check why the output and error handling here is different than in `MigrateDeploy`.
    // Automatically create the database if it doesn't exist
    const successMessage = await ensureDatabaseExists(
      baseDir,
      getSchemaDatasourceProvider(schemaContext),
      validatedConfig,
    )
    if (successMessage) {
      process.stdout.write('\n' + successMessage + '\n')
    }

    process.stdout.write('\n')
    if (!args['--force']) {
      if (!canPrompt()) {
        throw new MigrateResetEnvNonInteractiveError()
      }

      const confirmation = await prompt({
        type: 'confirm',
        name: 'value',
        message: `Are you sure you want to reset your database? ${red('All data will be lost')}.`,
      })

      process.stdout.write('\n') // empty line

      if (!confirmation.value) {
        process.stdout.write('Reset cancelled.\n')
        // Return SIGINT exit code to signal that the process was cancelled
        process.exit(130)
      }
    }

    aiAgentConfirmationCheckpoint()

    const schemaFilter: MigrateTypes.SchemaFilter = {
      externalTables: config.tables?.external ?? [],
      externalEnums: config.enums?.external ?? [],
    }

    const migrate = await Migrate.setup({
      schemaEngineConfig: config,
      baseDir,
      migrationsDirPath,
      schemaContext,
      schemaFilter,
      extensions: config['extensions'],
    })

    let migrationIds: string[]
    try {
      await migrate.reset()

      const { appliedMigrationNames } = await migrate.applyMigrations()
      migrationIds = appliedMigrationNames
    } finally {
      // Stop engine
      await migrate.stop()
    }

    if (migrationIds.length === 0) {
      process.stdout.write(`${green('Database reset successful\n')}\n`)
    } else {
      process.stdout.write('\n') // empty line
      process.stdout.write(
        `${green('Database reset successful')}

The following migration(s) have been applied:\n\n${printFilesFromMigrationIds('migrations', migrationIds, {
          'migration.sql': '',
        })}\n`,
      )
    }

    return ``
  }

  public help(error?: string): string | HelpError {
    if (error) {
      return new HelpError(`\n${bold(red(`!`))} ${error}\n${MigrateReset.help}`)
    }
    return MigrateReset.help
  }
}
